package cn.iocoder.yudao.module.vote.service.voteinfo;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.vote.constants.Const;
import cn.iocoder.yudao.module.vote.constants.QrCodeColorEnum;
import cn.iocoder.yudao.module.vote.constants.RedisConst;
import cn.iocoder.yudao.module.vote.controller.admin.userref.vo.VoteUserRefCreateReqVO;
import cn.iocoder.yudao.module.vote.controller.admin.voteinfo.vo.*;
import cn.iocoder.yudao.module.vote.controller.admin.voteuserresultdetail.vo.VoteUserResultDetailCreateReqVO;
import cn.iocoder.yudao.module.vote.convert.userref.VoteUserRefConvert;
import cn.iocoder.yudao.module.vote.convert.voteinfo.VoteInfoConvert;
import cn.iocoder.yudao.module.vote.convert.voteuserresultdetail.VoteUserResultDetailConvert;
import cn.iocoder.yudao.module.vote.dal.dataobject.userref.VoteUserRefDO;
import cn.iocoder.yudao.module.vote.dal.dataobject.voteinfo.VoteInfoDO;
import cn.iocoder.yudao.module.vote.dal.dataobject.voteuserresult.VoteUserResultDO;
import cn.iocoder.yudao.module.vote.dal.dataobject.voteuserresultdetail.VoteUserResultDetailDO;
import cn.iocoder.yudao.module.vote.dal.mysql.userref.VoteUserRefMapper;
import cn.iocoder.yudao.module.vote.dal.mysql.voteinfo.VoteInfoMapper;
import cn.iocoder.yudao.module.vote.dal.mysql.voteuserresult.VoteUserResultMapper;
import cn.iocoder.yudao.module.vote.service.userref.VoteUserRefService;
import cn.iocoder.yudao.module.vote.service.voterecord.VoteRecordService;
import cn.iocoder.yudao.module.vote.service.voteuserresultdetail.VoteUserResultDetailService;
import cn.iocoder.yudao.module.vote.utils.AesUtil;
import cn.iocoder.yudao.module.vote.utils.RedisService;
import com.alibaba.fastjson.JSONObject;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;
import org.springframework.util.ResourceUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.vote.enums.ErrorCodeConstants.*;

/**
 * 投票规则 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class VoteInfoServiceImpl implements VoteInfoService {

    @Resource
    private VoteInfoMapper infoMapper;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private VoteUserRefMapper voteUserRefMapper;


    @Resource
    private VoteUserResultMapper resultMapper;

    @Resource
    private VoteRecordService voteRecordService;

    @Resource
    private VoteUserResultDetailService resultDetailService;

    @Resource
    private RedisService redisService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer createInfo(VoteInfoCreateReqVO createReqVO) {

        // 插入
        VoteInfoDO info = VoteInfoConvert.INSTANCE.convert(createReqVO);
        infoMapper.insert(info);

        // 返回
        return info.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo(VoteInfoUpdateReqVO updateReqVO) {
        // 校验存在
        this.validateInfoExists(updateReqVO.getId());
        // 更新
        VoteInfoDO updateObj = VoteInfoConvert.INSTANCE.convert(updateReqVO);

        infoMapper.updateById(updateObj);
        // 更新到缓存
        updateVoteToRedis(updateObj);
    }

    /**
     * 更新到缓存
     * @param updateObj
     */
    private void updateVoteToRedis(VoteInfoDO updateObj){
        VoteInfoDO update = infoMapper.selectById(updateObj.getId());
        // 更新缓存
        String key = StrUtil.format(RedisConst.VOTE_INFO,update.getId());
        Long expireTime = DateUtil.between(new Date(),update.getEndTime(), DateUnit.SECOND);
        redisService.set(key,update);
        redisService.expire(key,expireTime, TimeUnit.SECONDS);
    }

    @Override
    public void pushState(VoteInfoUpdateReqVO updateReqVO) {
        VoteInfoDO voteInfoDO = getInfo(updateReqVO.getId());
        if(voteInfoDO==null){
            throw exception(INFO_NOT_EXISTS);
        }
        // 更新
        VoteInfoDO updateObj = VoteInfoConvert.INSTANCE.convert(updateReqVO);
        infoMapper.updateById(updateObj);
        // 更新缓存
        voteInfoDO.setIsPush(updateObj.getIsPush());
        updateVoteToRedis(voteInfoDO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInfo(Integer id) {
        // 校验存在
        this.validateInfoExists(id);
        // 删除
        infoMapper.deleteById(id);
        // 删除用户
        List<VoteUserResultDO> resultDOList = resultMapper.getByVoteId(id);
        if (CollUtil.isNotEmpty(resultDOList)) {
            for(VoteUserResultDO resultDO:resultDOList){
                resultMapper.deleteById(resultDO.getId());
            }
        }
        // 删除投票信息
        voteRecordService.deleteByVoteId(id);
    }

    private void validateInfoExists(Integer id) {
        if (infoMapper.selectById(id) == null) {
            throw exception(INFO_NOT_EXISTS);
        }
    }

    @Override
    public VoteInfoDO getInfo(Integer id) {
        VoteInfoDO info =infoMapper.selectById(id);
        return info;
    }

    @Override
    public List<VoteInfoDO> getInfoList(Collection<Integer> ids) {
        return infoMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<VoteInfoDO> getInfoPage(VoteInfoPageReqVO pageReqVO) {
        return infoMapper.selectPage(pageReqVO);
    }

    @Override
    public List<VoteInfoDO> getByUser(Integer voteType){
        AdminUserRespDTO respDTO = adminUserApi.getUser(SecurityFrameworkUtils.getLoginUserId());
        List<VoteInfoDO> voteInfoList = new ArrayList<>();
        List<VoteUserRefDO> voteUserRefList = voteUserRefMapper.getByUserId(respDTO.getId());
        if(voteUserRefList==null){
            return voteInfoList;
        }
        Date date = new Date();
        for(VoteUserRefDO userRef:voteUserRefList){
           VoteInfoDO voteInfoDO = infoMapper.selectById(userRef.getVoteId());
           if(voteInfoDO==null || voteInfoDO.getDeleted()){
               continue;
           }
           if (!voteType.equals(voteInfoDO.getVoteType())) {
               continue;
           }
           if (date.before(voteInfoDO.getBeginTime()) || date.after(voteInfoDO.getEndTime())) {
               continue;
           }
           voteInfoDO.setIsVote(false);
           if(voteRecordService.isVote(userRef.getVoteId().intValue())){
               voteInfoDO.setIsVote(true);
           }
           if(voteInfoDO.getWeight()==null){
               voteInfoDO.setWeight(1);
           }
           voteInfoList.add(voteInfoDO);
        }

        if(CollUtil.isNotEmpty(voteInfoList)){
            voteInfoList.sort(Comparator.comparing(VoteInfoDO::getWeight).thenComparing(VoteInfoDO::getVoteName).reversed());
        }

        return voteInfoList;
    }



    @Override
    public List<VoteInfoDO> getInfoList(VoteInfoExportReqVO exportReqVO) {
        return infoMapper.selectList(exportReqVO);
    }

    @Override
    public VoteInfoDO getCurrVote(Integer voteType){
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        VoteInfoDO voteInfoDO =  infoMapper.getCurrVote(voteType,user.getTenantId());
        if(voteInfoDO==null){
            exception(INFO_TIME_NOTSTART);
        }
        return voteInfoDO;
    }

    @Override
    public String getQrCode(OnlineVoteParamsVO paramsVO){
        validateInfoExists(paramsVO.getVoteId());

        String params = AesUtil.encrypt(JSONObject.toJSONString(paramsVO));
        String url = StrUtil.format(Const.VOTE_QRCODE_URL, params);
        QrConfig config = new QrConfig(Const.QRCODE_WIDTH,Const.QRCODE_HEIGHT);
        // 设置边距，既二维码和背景之间的边距
        config.setMargin(2);
        config.setErrorCorrection(ErrorCorrectionLevel.H);
//        if (BooleanUtil.isTrue(paramsVO.getIsDept())) {
//            config.setForeColor(QrCodeColorEnum.COLOR_DEPT.getColor());
//        } else if (BooleanUtil.isTrue(paramsVO.getIsManager())) {
//            config.setForeColor(QrCodeColorEnum.COLOR_MANAGER.getColor());
//        }

        BufferedImage bufferedImage = QrCodeUtil.generate(url,config);
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImgUtil.writePng(bufferedImage, out);

        return Base64Utils.encodeToString(out.toByteArray());
    }





    @Override
    @Transactional(rollbackFor = Exception.class)
    public void copyVote(VoteInfoUpdateReqVO pageVO){
        VoteInfoDO voteInfoDO = infoMapper.selectById(pageVO.getId());
        voteInfoDO.setId(null);
        voteInfoDO.setCreateTime(new Date());
        voteInfoDO.setUpdateTime(new Date());
        voteInfoDO.setBeginTime(DateUtil.offsetMonth(new Date(),-1));
        voteInfoDO.setEndTime(DateUtil.offsetMonth(new Date(),-1));
        voteInfoDO.setVoteName(pageVO.getVoteName());
        voteInfoDO.setVoteCount(0);
        voteInfoDO.setImportCount("0");
        voteInfoDO.setIsScope(false);
        voteInfoDO.setQuoteVoteId(null);
        voteInfoDO.setIsPush(false);
        infoMapper.insert(voteInfoDO);


        if(pageVO.getCopyBeVoter()!=null && pageVO.getCopyBeVoter()){
            List<VoteUserResultDO> resultList = resultMapper.getByVoteId(pageVO.getId());
            for(VoteUserResultDO result:resultList){

                VoteUserResultDetailDO detailDO=resultDetailService.selectByResultId(result.getId().longValue());
                result.setId(null);
                result.setVoteId(voteInfoDO.getId());
                result.setVoteName(voteInfoDO.getVoteName());
                result.setTotal(0);
                result.setAvgScore(0f);
                result.setQuantifiedScore(0f);
                result.setPartyLeaderScore(0D);
                result.setOtherLeaderScore(0D);
                result.setTeamLeaderScore(0D);
                result.setTeamMemberScore(0D);
                result.setCreateTime(new Date());
                result.setUpdateTime(new Date());
                resultMapper.insert(result);
                if(detailDO!=null) {
                    VoteUserResultDetailCreateReqVO detailCreateReqVO = VoteUserResultDetailConvert.INSTANCE.convert2(detailDO);
                    detailCreateReqVO.setResultId(result.getId().longValue());
                    resultDetailService.createUserResultDetail(detailCreateReqVO);
                }

            }
            voteInfoDO.setImportCount(resultList.size()+"");
            infoMapper.updateById(voteInfoDO);
        }
    }

}
